home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Mac Game Programming Gurus / TricksOfTheMacGameProgrammingGurus.iso / CodeWarrior Lite / Metrowerks C⁄C++ Lite / Libraries / DebugNew / DebugNew.cp next >
Encoding:
Text File  |  1995-04-28  |  12.6 KB  |  524 lines  |  [TEXT/MMCC]

  1. /*
  2.  *  DebugNew.cp
  3.  *
  4.  *  Copyright © 1993 metrowerks inc.  All rights reserved.
  5.  * 
  6.  *    A debugging layer over the standard operator new and delete
  7.  *  to detect memory overwrites, incorrect deletes, and memory leaks.
  8.  *  See DebugNew.doc in the CodeWarrior Examples:Debug New demo: for 
  9.  *  background and usage instructions.
  10.  */
  11.  
  12. #include "DebugNew.h"
  13.  
  14. // If leak checking is enabled, we need to define macros that
  15. // will allow us to replace the usual operator new and operator delete
  16. #if DEBUG_NEW >= DEBUG_NEW_BASIC
  17.  
  18. #define OPERATOR_NEW        _std_operator_new
  19. #define OPERATOR_DELETE        _std_operator_delete
  20.  
  21. void *_std_operator_new(size_t size);
  22. void *_std_operator_new(size_t size,void *p);
  23. void _std_operator_delete(void *ptr);
  24.  
  25. #endif
  26.  
  27. #include <New.cp>
  28.  
  29. #if DEBUG_NEW >= DEBUG_NEW_BASIC
  30.  
  31.  
  32.     // For every block allocated, we bump up the size to allow a header
  33.     // and trailer to be put on the block. The header has bookkeeping info
  34.     // to allow us to know if the block was probably a real allocated block,
  35.     // and size info to allow us to find the trailer. The trailer is used to
  36.     // detect overwrites.
  37.     // NOTE: magic, unlikely values are used to tag blocks. This is a heuristic
  38.     // only, since programs could legitimately be writing those same values through
  39.     // wild pointers. When leak testing is on we can do better, since we have a
  40.     // list of allocated blocks. The tag method provides reasonable checking 
  41.     // with less overhead.
  42.     
  43.     struct BlockHeader
  44.     {
  45.     #if DEBUG_NEW == DEBUG_NEW_LEAKS
  46.         BlockHeader* next;
  47.         const char*  file;
  48.         int             line;
  49.     #endif
  50.         size_t        size; // size of user's block in bytes, doesn't include prefix/suffix
  51.         long        tag;  // magic value identifying the block
  52.     };
  53.     
  54.     struct BlockTrailer
  55.     {
  56.         long tag;           // magic value at end, used to detect overwrites
  57.     };
  58.     
  59.     enum BlockStatus_t    
  60.     {
  61.         blockValid,                  // valid allocated block
  62.         blockFree,                  // free block
  63.         blockPtrNull,              // block pointer is NULL
  64.         blockPtrOutOfRange,     // block pointer outside application heap
  65.         blockBadHeader,            // block has invalid header
  66.         blockBadTrailer,        // block has invalid trailer
  67.         blockNotInList,            // block appears valid, but is not in block list (leak checking only)
  68.         blockFreeOverwritten     // dnDontFreeBlocks enabled, zapped free block was overwritten
  69.     };    
  70.  
  71.     
  72. static void             DefaultErrorHandler(short);
  73. static void*             alloc_block(size_t size, const char* file, int line);
  74. static void             ZapBlock(void* ptr, register size_t len, register long value);
  75. static BlockStatus_t     GetBlockStatus(char* ptr, Boolean fValidateFree);
  76. static void             ReportBlockStatus(BlockStatus_t status);
  77.  
  78.     // global data used by DebugNew
  79.     
  80. unsigned long                         gDebugNewAllocCount; // these three variables are public
  81. unsigned long                        gDebugNewAllocCurr;  // total #bytes currently allocated
  82. unsigned long                        gDebugNewAllocMax;     // max #bytes ever allocated
  83.  
  84. static void*                         gHeapBegin;
  85. static void*                         gHeapEnd;
  86. static DebugNewErrorHandler_t        gErrorHandler = DefaultErrorHandler;
  87.  
  88. #if DEBUG_NEW == DEBUG_NEW_LEAKS
  89. static BlockHeader*                 gBlockList;
  90. #endif
  91.  
  92. unsigned long                        gDebugNewFlags;
  93.  
  94. #define      BLOCK_HEADER_SIZE    (sizeof(BlockHeader))
  95. #define      BLOCK_TRAILER_SIZE (sizeof(BlockTrailer))
  96. #define      BLOCK_OVERHEAD    (BLOCK_HEADER_SIZE + BLOCK_TRAILER_SIZE)
  97.  
  98. // Block zapping values. Newly allocated and free blocks are filled with
  99. // these values to detect uses of uninitialize memory and released memory.
  100. // Different values are used so you can look at a piece of memory
  101. // in the debugger and tell if it is allocated but uninitialized or
  102. // if it was released.
  103. #define      ZAP_UNINITIALIZED    0xF1F1F1F1L
  104. #define      ZAP_RELEASED        0xF3F3F3F3L
  105.  
  106.  
  107. // some random, hopefully not too common, odd values 
  108. #define      BLOCK_PREFIX_ALLOC        0xD1F3D1F3L    
  109. #define      BLOCK_PREFIX_FREE            0xF7B9F7B9L
  110. #define      BLOCK_SUFFIX                0xB7D5B7D5L
  111.  
  112.  
  113. void *operator new(size_t size)
  114. {
  115.     return alloc_block(size, 0, 0);
  116. }
  117.  
  118. #if DEBUG_NEW == DEBUG_NEW_LEAKS
  119.  
  120. void* operator new(size_t size, const char* file, int line)
  121. {
  122.     return alloc_block(size, file, line);
  123. }
  124.  
  125. #endif
  126.  
  127. void operator delete(void* ptr)
  128. {
  129.         // Allocation counter should be > zero. If not, then delete was
  130.         // called too many times.
  131.     if (!gDebugNewAllocCount)
  132.     {
  133.         gErrorHandler(dbgnewTooManyFrees);
  134.         return;
  135.     }
  136.     
  137.     BlockStatus_t status = GetBlockStatus((char*)ptr, false);
  138.     switch (status)
  139.     {
  140.         case blockFree:
  141.         case blockPtrOutOfRange:
  142.         case blockBadHeader:
  143.         case blockBadTrailer:
  144.             ReportBlockStatus(status);
  145.             return;
  146.             
  147.         case blockPtrNull:     // it's legal to pass NULL to operator delete
  148.             return;        
  149.     }
  150.     
  151.     BlockHeader* h = (BlockHeader*)((char*)ptr - BLOCK_HEADER_SIZE);
  152.     const Boolean fDoFree = !(gDebugNewFlags & dnDontFreeBlocks);
  153.  
  154. #if DEBUG_NEW == DEBUG_NEW_LEAKS
  155.         // find the block in the block list
  156.         BlockHeader* curr = gBlockList;
  157.         BlockHeader* prev = 0;
  158.         while (curr)
  159.         {
  160.             if (curr == h)
  161.                 break;
  162.             prev = curr;
  163.             curr = curr->next;    
  164.         }
  165.         if (!curr)
  166.         {
  167.             gErrorHandler(dbgnewBlockNotInList);
  168.             return;
  169.         }
  170.         else if (fDoFree)
  171.         {
  172.             if (prev)
  173.                 prev->next = curr->next;
  174.             else
  175.                 gBlockList = curr->next;
  176.             curr->next = 0;
  177.         }
  178. #endif
  179.          ZapBlock(ptr, h->size + BLOCK_TRAILER_SIZE, ZAP_RELEASED);
  180.                    
  181.            // mark block freed, and free it
  182.            h->tag = BLOCK_PREFIX_FREE;
  183.            if (fDoFree)
  184.            {
  185.                --gDebugNewAllocCount;
  186.                gDebugNewAllocCurr -= h->size;
  187.             _std_operator_delete((char*)ptr - BLOCK_HEADER_SIZE);  
  188.         }
  189. }
  190.  
  191. static void* alloc_block(size_t size, const char* file, int line)
  192. {        
  193.         // Use the standard allocator. It calls the new_handler if needed,
  194.         // so if it fails, the allocation really failed.
  195.         
  196.     char* p = (char*) _std_operator_new(size+BLOCK_OVERHEAD);
  197.     if (!p)
  198.         return 0;
  199.         
  200.     ++gDebugNewAllocCount;
  201.     gDebugNewAllocCurr += size;
  202.     if (gDebugNewAllocCurr > gDebugNewAllocMax)
  203.         gDebugNewAllocMax = gDebugNewAllocCurr;
  204.  
  205.         // zap data area + trailer, so blocks of sizes that are
  206.         // not multiples of four get fully zapped.
  207.         
  208.     ZapBlock(p+BLOCK_HEADER_SIZE, size+BLOCK_TRAILER_SIZE, ZAP_UNINITIALIZED);
  209.     
  210.     BlockHeader* h = (BlockHeader*)p;
  211.     h->size = size;
  212.     h->tag = BLOCK_PREFIX_ALLOC;
  213.     
  214.     BlockTrailer* t = (BlockTrailer*)(p + size + BLOCK_HEADER_SIZE);
  215.     t->tag = BLOCK_SUFFIX;
  216.     
  217. #if DEBUG_NEW == DEBUG_NEW_LEAKS
  218.         // new blocks go to front of the block list
  219.     h->next = gBlockList;
  220.     gBlockList = h;
  221.     h->file = file;
  222.     h->line = line;
  223. #endif
  224.     return p+BLOCK_HEADER_SIZE;
  225. }
  226.  
  227.  
  228. static BlockStatus_t GetBlockStatus(char* ptr, Boolean fValidateFree)
  229. {
  230.     if (!ptr)
  231.         return blockPtrNull;
  232.         
  233.         // Validate that pointer is in the application heap. This
  234.         // will also trap attempts to free pointers whose values
  235.         // are inside uninitialized or free blocks, i.e.
  236.         // 0xF1F1F1F1 or 0xF3F3F3F3.
  237.         
  238.     if ((void*)ptr < &ApplicationZone()->heapData || ptr > ApplicationZone()->bkLim)
  239.         return blockPtrOutOfRange;
  240.  
  241.     const BlockHeader& h = *(BlockHeader*)(ptr - BLOCK_HEADER_SIZE);
  242.     
  243.     // check block header
  244.             
  245.     if (h.tag == BLOCK_PREFIX_FREE)
  246.     {
  247.         if (fValidateFree)
  248.         {
  249.                 // Check that free block contains the zap values, if not
  250.                 // then it was probably overwritten through a dangling
  251.                 // pointer
  252.                 
  253.             const unsigned long* p = (const unsigned long*) ((char*)&h + BLOCK_HEADER_SIZE);
  254.             long count = (h.size+BLOCK_TRAILER_SIZE) / 4;
  255.             while (--count >= 0)
  256.             {
  257.                 if (*p++ != ZAP_RELEASED)
  258.                     return blockFreeOverwritten;
  259.             }
  260.         }
  261.         return blockFree;
  262.     }
  263.         
  264.     if (h.tag != BLOCK_PREFIX_ALLOC)
  265.         return blockBadHeader;
  266.         
  267.     const BlockTrailer& t = *(BlockTrailer*) (ptr + h.size);
  268.  
  269.     // check block trailer
  270.     
  271.     if (t.tag != BLOCK_SUFFIX)
  272.         return blockBadTrailer;
  273.         
  274.     return blockValid;
  275. }
  276.  
  277. static void ReportBlockStatus(BlockStatus_t status)
  278. {
  279.     switch (status)
  280.     {
  281.         case blockFree:
  282.             gErrorHandler(dbgnewFreeBlock);
  283.             break;
  284.             
  285.         case blockPtrNull:
  286.             gErrorHandler(dbgnewNullPtr);
  287.             break;
  288.         
  289.         case blockPtrOutOfRange:
  290.             gErrorHandler(dbgnewPointerOutsideHeap);
  291.             break;
  292.             
  293.         case blockBadHeader:
  294.             gErrorHandler(dbgnewBadHeader);
  295.             break;
  296.         
  297.         case blockBadTrailer:
  298.             gErrorHandler(dbgnewBadTrailer);
  299.             break;
  300.             
  301.         case blockNotInList:
  302.             gErrorHandler(dbgnewBlockNotInList);
  303.             break;
  304.             
  305.         case blockFreeOverwritten:
  306.             gErrorHandler(dbgnewFreeBlockOverwritten);
  307.             break;
  308.     }
  309. }
  310.  
  311. DebugNewErrorHandler_t DebugNewSetErrorHandler(DebugNewErrorHandler_t newHandler)
  312. {
  313.     DebugNewErrorHandler_t oldHandler = gErrorHandler;
  314.     if (newHandler)
  315.         gErrorHandler = newHandler;
  316.     return oldHandler;
  317. }
  318.  
  319. static void DefaultErrorHandler(short err)
  320. {
  321.     const unsigned char* s;
  322.     switch (err)
  323.     {
  324.         case dbgnewNullPtr:
  325.             s = "\pDebugNew: null pointer";
  326.             break;
  327.             
  328.         case dbgnewTooManyFrees:
  329.             s = "\pDebugNew: more deletes than news";
  330.             break;
  331.             
  332.         case dbgnewPointerOutsideHeap:
  333.             s = "\pDebugNew: delete or validate called for pointer outside application heap";
  334.             break;
  335.             
  336.         case dbgnewFreeBlock:
  337.             s = "\pDebugNew: delete or validate called for free block";
  338.             break;
  339.             
  340.         case dbgnewBadHeader:
  341.             s = "\pDebugNew: unknown block, or block header was overwritten";
  342.             break;
  343.             
  344.         case dbgnewBadTrailer:
  345.             s = "\pDebugNew: block trailer was overwritten";
  346.             break;
  347.             
  348.         case dbgnewBlockNotInList:
  349.             s = "\pDebugNew: block valid but not in block list (internal error)";
  350.             break;
  351.             
  352.         case dbgnewFreeBlockOverwritten:
  353.             s = "\pDebugNew: free block overwritten, could be dangling pointer";
  354.             break;
  355.                                     
  356.         default:
  357.             s = "\pDebugNew: undefined error";
  358.             break;
  359.     }
  360.     DebugStr68k(s);
  361. }
  362.  
  363.  
  364. static void ZapBlock(void* ptr, register size_t len, register long value)
  365. {
  366.     register long* p = (long*) ptr;
  367.     len /= sizeof(long);
  368.     while (len-- > 0)
  369.         *p++ = value;
  370. }
  371.  
  372. #if DEBUG_NEW == DEBUG_NEW_LEAKS
  373.  
  374. void  DebugNewValidateAllBlocks()
  375. {
  376.     // traverse the block list, and validate every block
  377.     BlockHeader* curr = gBlockList;
  378.     while (curr)
  379.     {
  380.         BlockStatus_t status = GetBlockStatus((char*)curr + BLOCK_HEADER_SIZE, true);
  381.         if (status != blockValid && status != blockFree)
  382.         {
  383.             ReportBlockStatus(status);
  384.             // abort on any error. Not strictly necessary for some errors,
  385.             // but we could crash if next field is trashed.
  386.             break;
  387.         }        
  388.         curr = curr->next;
  389.     }
  390. }
  391.  
  392. #include <stdio.h>
  393. #include <Files.h>
  394. #include <Strings.h>
  395. #include <Processes.h>
  396.  
  397. static FILE *FSSpecOpen(FSSpec& fsspec, const char *mode)
  398. {    
  399.     FILE* file = 0;
  400.     short savedVol;
  401.     OSErr err;
  402.     
  403.     err = GetVol(0, &savedVol);
  404.     if (err == noErr)
  405.     {
  406.         err = HSetVol(0, fsspec.vRefNum, fsspec.parID);
  407.         if (err == noErr)
  408.         {
  409.             p2cstr(fsspec.name);
  410.             file = fopen((char*)fsspec.name, mode);
  411.             c2pstr((char*)fsspec.name);
  412.         }
  413.         SetVol(0, savedVol);
  414.     }
  415.     return file;
  416. }
  417.  
  418.  
  419. void DebugNewReportLeaks()
  420. {
  421.     ProcessSerialNumber psn;
  422.     OSErr err = GetCurrentProcess(&psn);
  423.     if (err != noErr) return;
  424.     
  425.     ProcessInfoRec info;
  426.     FSSpec spec;
  427.     
  428.     info.processInfoLength = sizeof(info);
  429.     info.processName = 0;
  430.     info.processAppSpec = &spec;
  431.     err = GetProcessInformation(&psn, &info);
  432.     if (err != noErr) return;
  433.     
  434.     StringPtr s = "\pleaks.log";
  435.     BlockMove(s, spec.name, s[0]+1);
  436.     FILE* f = FSSpecOpen(spec, "w");    
  437.     if (!f) return;
  438.  
  439.     long count = 0;
  440.     long leakCount = 0;
  441.     unsigned long bytesLeaked = 0;
  442.     BlockHeader* curr = gBlockList;
  443.     while (curr)
  444.     {
  445.         ++count;
  446.         if (curr->tag == BLOCK_PREFIX_ALLOC)
  447.         {
  448.             bytesLeaked += curr->size;
  449.             ++leakCount;
  450.         }
  451.         curr = curr->next;
  452.     }
  453.     
  454.     if(count != gDebugNewAllocCount)
  455.         fprintf(f, "Warning: length of block list different from count of allocated blocks (internal error).\n");
  456.         
  457.     fprintf(f, "Maximum #bytes allocated at any point via operator new: %ld\n", gDebugNewAllocMax);
  458.     
  459.     if (!leakCount)
  460.     {
  461.         fprintf(f, "No memory leaks.\n");
  462.         return;
  463.     }
  464.     if (leakCount == 1)
  465.         fprintf(f,"There is 1 memory leak of %lu bytes:\n", bytesLeaked);
  466.     else
  467.         fprintf(f,"There are %ld memory leaks, totaling %lu bytes:\n", leakCount, bytesLeaked);
  468.     
  469.     unsigned long totalAlloc = 0;
  470.     
  471.     curr = gBlockList;
  472.     while (curr)
  473.     {
  474.         if (curr->tag == BLOCK_PREFIX_ALLOC)
  475.         {
  476.             if (curr->file)
  477.                 fprintf(f,"  %s line: %d, size: %lu\n", curr->file, curr->line, curr->size);
  478.             else
  479.                 fprintf(f,"  <unknown>, size: %lu\n", curr->size);
  480.         }
  481.         totalAlloc += curr->size; // count freed blocks, since gDebugNewAllocCurr does
  482.         curr = curr->next;
  483.     }
  484.     if (totalAlloc != gDebugNewAllocCurr)
  485.         fprintf(f, "Warning: total allocations in block list different from gDebugNewAllocCurr.\n");
  486.  
  487.     fclose(f);
  488. }
  489. #else
  490.     // leak checking disabled, does nothing
  491. void DebugNewReportLeaks()
  492. {
  493. }
  494. #endif    // DEBUG_NEW_LEAKS
  495.  
  496. #endif  //  DEBUG_NEW >= DEBUG_NEW_BASIC
  497.  
  498. void DebugNewValidatePtr(void* ptr)
  499. {
  500. #if DEBUG_NEW >= DEBUG_NEW_BASIC
  501.     BlockStatus_t status = GetBlockStatus((char*)ptr, true);
  502. #if DEBUG_NEW == DEBUG_NEW_LEAKS
  503.     if (status == blockValid)
  504.     {
  505.         const BlockHeader* h = (BlockHeader*)((char*)ptr - BLOCK_HEADER_SIZE);
  506.  
  507.         // find the block in the block list
  508.         BlockHeader* curr = gBlockList;
  509.         while (curr)
  510.         {
  511.             if (curr == h)
  512.                 break;
  513.             curr = curr->next;    
  514.         }
  515.         if (!curr)
  516.             status = blockNotInList;
  517.     }
  518. #endif
  519.     if (status != blockValid)
  520.         ReportBlockStatus(status);
  521. #endif
  522. }
  523.  
  524.